﻿using System.Collections.Generic;
using System.Diagnostics;
using CI.Interfaces;

namespace CI.Utils
{
    /// <summary>
    /// Statistics logger
    /// </summary>
    public static class StatsLogger
    {

        /// <summary>
        /// List of sleeping stats
        /// </summary>
        private static readonly List<StatsSleeper> StatsSleeperList = new List<StatsSleeper>();
        private static readonly SimEnv SimEnv = SimEnv.Instance;
        private const int SLEEP_TIME = 1000;
        private const int HIGH_SLEEP_TIME_RESTART = 300000;

        // cpu usage monitor
        private static readonly PerformanceCounter CpuUsage = new PerformanceCounter
                                                                  {
                                                                      CategoryName = "Processor",
                                                                      CounterName = "% Processor Time",
                                                                      InstanceName = "_Total"
                                                                  };

        private static readonly SimEnv _simEnv = SimEnv.Instance;

        /// <summary>
        /// Sleep for specified amount of time and log it to the stats
        /// </summary>
        /// <param name="milliseconds">time in milliseconds to sleep</param>
        public static void Sleeper(int milliseconds)
        {
#if DEBUG
            var statsSleeper = new StatsSleeper { CPUStart = CpuUsage.NextValue(), SleepTime = milliseconds };
            var stackTrace = new StackTrace();
            statsSleeper.StackFrame = stackTrace.GetFrames();
#endif
            _simEnv.TraceLogger.LogDebug(string.Format("Sleeping  for {0} milliseconds", milliseconds));
            System.Threading.Thread.Sleep(milliseconds);
            _simEnv.TraceLogger.LogDebug("Sleep completed");
#if DEBUG
            statsSleeper.CPUFinish = CpuUsage.NextValue();
            StatsSleeperList.Add(statsSleeper);
#endif
        }

        /// <summary>
        /// Sleep until cpu usage is below a certain value
        /// </summary>
        /// <param name="maxCpuUsage">target cpu usage to get below</param>
        public static void SleepCPUBelow(int maxCpuUsage)
        {
#if DEBUG
            var statsSleeper = new StatsSleeper { CPUStart = CpuUsage.NextValue() };
            var sw = new Stopwatch();
            sw.Start();
#endif
            float cpuUsage;
            float totalCPUSleepTime = 0;

            while (((cpuUsage = CpuUsage.NextValue()) > maxCpuUsage) && (!SimEnv.BreakExit))
            {
                // let them know whats happening

                string status = string.Format("High CPU {0}, Sleeping for {1} milliseconds", cpuUsage, SLEEP_TIME);
                SimEnv.HostEnv.ProcessingState = status;

                _simEnv.TraceLogger.LogDebug(status);
                System.Threading.Thread.Sleep(SLEEP_TIME);
                totalCPUSleepTime += SLEEP_TIME;
                if (totalCPUSleepTime > HIGH_SLEEP_TIME_RESTART)
                {
                    const string errorMsg = "High CPU Waited for max time, restarting";
                    _simEnv.TraceLogger.LogWarn(errorMsg);
                    SimEnv.TriggerScenarioRestart(errorMsg);
                }
            }
#if DEBUG
            sw.Stop();
            statsSleeper.SleepTime = sw.ElapsedMilliseconds;
            var stackTrace = new StackTrace();
            statsSleeper.StackFrame = stackTrace.GetFrames();
            statsSleeper.CPUFinish = CpuUsage.NextValue();
            StatsSleeperList.Add(statsSleeper);
            _simEnv.TraceLogger.LogDebug(string.Format("SleepCPUBelow  for {0} milliseconds", sw.ElapsedMilliseconds));
#endif

        }

        /// <summary>
        /// Log the stats totals
        /// </summary>
        public static void TotalStats()
        {
            long MaxSleepTime = 0;
            long TotalSleepTime = 0;
            float AverageStartCPU = 0;
            float AverageEndCPU = 0;

            foreach (StatsSleeper sleep in StatsSleeperList)
            {
                TotalSleepTime += sleep.SleepTime;
                AverageStartCPU += sleep.CPUStart;
                AverageEndCPU += sleep.CPUFinish;

                if (sleep.SleepTime > MaxSleepTime)
                    MaxSleepTime = sleep.SleepTime;
            }

            _simEnv.TraceLogger.LogDebug(string.Format("Max Sleep Time {0}", MaxSleepTime));
            _simEnv.TraceLogger.LogDebug(string.Format("Total Sleep Time {0}", TotalSleepTime));

            if (StatsSleeperList.Count > 0)
            {
                _simEnv.TraceLogger.LogDebug(string.Format("Average Sleep Time {0}", TotalSleepTime / StatsSleeperList.Count));
                _simEnv.TraceLogger.LogDebug(string.Format("Average CPU Start {0}", AverageStartCPU / StatsSleeperList.Count));
                _simEnv.TraceLogger.LogDebug(string.Format("Average CPU Finish {0}", AverageEndCPU / StatsSleeperList.Count));
                _simEnv.TraceLogger.LogDebug(string.Format("Average CPU Difference {0}", (AverageStartCPU / StatsSleeperList.Count) - (AverageEndCPU / StatsSleeperList.Count)));
                _simEnv.TraceLogger.LogDebug(string.Format("Slept {0} times", StatsSleeperList.Count));
            }




        }

    }
}


